package agent;

import common.Piece;
import common.Move;

import java.util.LinkedList;
import java.util.Random;

public class Agent {

    /**
     * Represents what the agent knows about the current state of the game.
     */
    private AgentCurrentState currentState;
    /**
     * The algorithm used by the agent.
     */    
    private GameAlgorithm algorithm;
    //The agent has an instance of HandsSimulator and not of Alpha-Beta because
    //HandsSimulator uses Alpha-Beta.
    private HandsSimulator handsSimulator;
    private RandomAlgorithm randomAlgorithm;
    /**
     * The random number generator used to by the agent.
     */    
    private Random random;

    public Agent(int agentSeed) {
        random = new Random(agentSeed);
        handsSimulator = new HandsSimulator();
        randomAlgorithm = new RandomAlgorithm();
        useAlfabeta();
    }

    /**
     * Sets the list of pieces received by the player in the beginning of the game.
     * @param pieces The list of pieces received by the agent.
     */    
    public void setInitialPiecesSet(LinkedList<Piece> pieces) {
        currentState = new AgentCurrentState(pieces);
    }

    /**
     * Called so that the agent can play.
     * The agent should not make modification to its current state in this method.
     * This is only done when the Game Manager notifies the agent of its own and
     * its oppnent's moves (see methods notifyAgentMove and notifyOpponentMove).
     * @return The move that the agent decides to play.
     */    
    public Move play() {
        return algorithm.takeDecision(currentState);
    }

    public final void useAlfabeta() {
        algorithm = handsSimulator;
        algorithm.setRandom(random);
    }

    public final void useRandomAlgorithm() {
        algorithm = randomAlgorithm;
        algorithm.setRandom(random);
    }

    /**
     * Sets the depth search when the agent uses the alpha-beta algorithm.
     * @param searchDepth The search depth.
     */    
    public void setSearchDepth(int searchDepth) {
        algorithm.setSearchDepth(searchDepth);
    }

    /**
     * Sets the number of hands to be simulated by the agent each time it must
     * take a decision using the alpha-beta algorithm.
     * @param handsLimit 
     */    
    public void setHandsLimit(int handsLimit) {
        algorithm.setHandsLimit(handsLimit);
    }

    /**
     * Informs the agent about its own last move. 
     * The agent must use this information to update its current state.
     * @param move 
     */    
    public void notifyAgentMove(Move move) {
        if (!currentState.placePiece(Player.AGENT, move.getSide(), move.getPiece())) {
            throw new IllegalArgumentException();
        }
    }

    /**
     * Informs the agent about its opponent's last move. 
     * The agent must use this information to update its current state.
     * @param move 
     */      
    public void notifyOpponentMove(Move move) {
        if (!currentState.placePiece(Player.OPPONENT, move.getSide(), move.getPiece())) {
            throw new IllegalArgumentException();
        }
    }
}
